package com.bblocks.common.anti_reptile.rateLimiter;


import com.bblocks.common.anti_reptile.config.bean.RuleComm;
import com.bblocks.common.anti_reptile.constant.AntiReptileConsts;
import com.bblocks.common.anti_reptile.constant.RuleResultEnum;
import com.bblocks.common.anti_reptile.constant.RuleTypeEnum;
import com.bblocks.common.anti_reptile.module.RuleStatVO;
import com.bblocks.common.anti_reptile.module.RuleVO;
import com.bblocks.common.anti_reptile.rule.RuleData;
import com.bblocks.common.anti_reptile.util.IpUtil;
import com.bblocks.common.anti_reptile.util.MD5Utils;
import com.bblocks.common.anti_reptile.util.StrUtil;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;

/**
 * <p>Project: bb-anti-reptile - RateLimiterPlanRedis</p>
 * <p>描述：基于redis的同步限流</p>
 *
 * @Author Zhao [125043150@qq.com]
 * @Date 2024/3/23 15:34
 * @Version 1.0
 * @since 8
 */

@ConditionalOnClass({ RedissonClient.class})
//@Component
@Slf4j
public class RateLimiterPlanRedis extends RateLimiterPlan {
    /**
     * 黑名单缓存key(类型变更-名字换个)
     */
    public static final String RATELIMITER_HIT_CRAWLERSTRATEGY = AntiReptileConsts.JOINER + "HIT_CRAWLERSTRATEGY_NEW";

    @Lazy
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private RuleData ruleData;
    @Resource
    private BlackListService blackListService;

    @PostConstruct
    public void init(){
    }

    public boolean tryAcquire(RuleTypeEnum ruleTypeEnum, String id, String url, long rate, long rateInterval, TimeUnit unit,RuleVO ruleVO) {
        RateIntervalUnit rateIntervalUnit = unit2RateIntervalUnit(unit);
        if (rate <= 0) {
            //需要提取对应的规则数据，如果未提取到则规则无限流
            if(ruleVO == null)
                ruleVO = ruleData.theRule(ruleTypeEnum, url, ruleTypeEnum == RuleTypeEnum.IP_PLUS ? id : null);
            if (ruleVO == null)
                return true;

            rate = ruleVO.getRateMax() * (rateIntervalUnit.toMillis(rateInterval) / 1000);//先用最大值把
            if (rate <= 0)//计算出0-暂时不限把
                return true;
        }

        //部分key会出现问题先md5处理下或者url处理下
        /*
        if (ruleTypeEnum == RuleTypeEnum.IP_PLUS || ruleTypeEnum == RuleTypeEnum.IP) {
            id = ip2Num(id);
        }
        String key = (new StringBuilder(AntiReptileConsts.BB_ANTI_PRE))
                .append(ruleTypeEnum.getType())
                .append(AntiReptileConsts.JOINER)
                .append(id)
                .append(AntiReptileConsts.JOINER)
                .append(url)
                .toString();*/
        String key = AntiReptileConsts.BB_ANTI_PRE +ruleTypeEnum.getType() + AntiReptileConsts.JOINER + StrUtil.base64Encode(id+url);

        // 1、 声明一个限流器
        RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);

        if(log.isDebugEnabled())
            log.debug("key:{},rate:{},rateInterval:{},rateIntervalUnit:{}", key, rate, rateInterval, rateIntervalUnit);
        // 2、 设置速率，expirationTime 时间中产生requestMaxSize个令牌
        rateLimiter.trySetRate(RateType.OVERALL, rate, rateInterval, rateIntervalUnit);

        // 3、试图获取一个令牌，获取到返回true
        return rateLimiter.tryAcquire(1);
    }



    public RuleResultEnum isBlack(RuleTypeEnum ruleTypeEnum, String id) {
        //RMapCache rHitMap = redissonClient.getMapCache(AntiReptileConsts.BB_ANTI_PRE + ruleTypeEnum.getType() + RATELIMITER_HIT_CRAWLERSTRATEGY);
        return blackListService.containBlack(ruleTypeEnum,id);
    }

    /**
     * 加入黑名单
     * @param rule
     * @param id
     * @param requestUrl
     * @param time
     * @param unit
     * @return
     */
    public int joinBlack(RuleComm rule, String id, String requestUrl, long time, TimeUnit unit,@NonNull RuleResultEnum theRuleResultEnum) {
        //RMapCache<String, String> rHitMap = redissonClient.getMapCache(AntiReptileConsts.BB_ANTI_PRE + rule.getRuleTypeEnum().getType() + RATELIMITER_HIT_CRAWLERSTRATEGY);
        //保存触发来源
        return blackListService.join(rule.getRuleTypeEnum(), id, requestUrl, time, unit,theRuleResultEnum);
    }

    /**
     * 退出黑名单
     *
     * @param rule
     * @param id
     * @param theRuleResultEnum
     * @return
     */
    public RuleResultEnum outBlack(RuleComm rule, String id,RuleResultEnum theRuleResultEnum) {
        //RMapCache rHitMap = redissonClient.getMapCache(AntiReptileConsts.BB_ANTI_PRE + rule.getRuleTypeEnum().getType() + RATELIMITER_HIT_CRAWLERSTRATEGY);
        return blackListService.out(rule.getRuleTypeEnum(), id,theRuleResultEnum);
    }

    /**
     * 转RateIntervalUnit，默认 RateIntervalUnit.MILLISECONDS
     * @param unit
     * @return
     */
    private RateIntervalUnit unit2RateIntervalUnit(TimeUnit unit) {
        if (unit == TimeUnit.SECONDS) {
            return RateIntervalUnit.SECONDS;
        } else if (unit == TimeUnit.MINUTES) {
            return RateIntervalUnit.MINUTES;
        } else if (unit == TimeUnit.HOURS) {
            return RateIntervalUnit.HOURS;
        } else if (unit == TimeUnit.DAYS) {
            return RateIntervalUnit.DAYS;
        } else
            return RateIntervalUnit.MILLISECONDS;
    }


    /**
     * 根据ip地址,转换成数字字符串(ip4以为的将保留原值)
     * @param ip
     * @return IP 尽量V4 地址
     */
    private  String ip2Num(String ip) {
        try {
            return String.valueOf(IpUtil.ipv4ToLong(ip));
        } catch (IllegalArgumentException e) {
            return ip.replace(".","");
        }
    }
}